﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace SharpCrack
{
	/// <summary>
	/// 
	/// </summary>
	[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1710:IdentifiersShouldHaveCorrectSuffix")]
	public class PermutationGenerator : IEnumerable<string>
	{
		public PermutationGenerator(string chrset, int length)
		{
			if (String.IsNullOrEmpty(chrset))
				throw new ArgumentException("Charset must contain at least one char.", "chrset");

			if (length <= 0)
				throw new ArgumentException("Permutation length must be a positive integer.", "length");

			this.Charset = chrset;
			this.Length = length;
			this.Cursor = new byte[this.Length];
		}

		internal byte[] Cursor;

		/// <summary>
		/// Gets or sets charset.
		/// </summary>
		public string Charset { get; set; }

		/// <summary>
		/// Gets or sets the length of the generated permutations.
		/// </summary>
		public int Length { get; set; }

		/// <summary>
		/// Gets the current permutation.
		/// </summary>
		public string Current
		{
			get
			{
				StringBuilder sb = new StringBuilder(this.Length);

				foreach (byte i in this.Cursor)
					sb.Append(this.Charset[i]);

				return sb.ToString();
			}
		}

		/// <summary>
		/// Sets starting permutation.
		/// </summary>
		/// <param name="perm"></param>
		public void SetStartingPermutation(string perm)
		{
			if (perm.Length != this.Length)
				throw new ArgumentException("Lengths must be equal.", "perm");

			for (int i = 0; i < perm.Length; i++)
			{
				int p = this.Charset.IndexOf(perm[i]);

				// Invalid char
				if (p == -1)
					throw new ArgumentException(String.Format("Not allowed character in starting permutation. Charset does not contain '{0}'.", perm[i]), "perm");

				this.Cursor[i] = (byte)p;
			}
		}

		/// <summary>
		/// Resets cursor to initial position.
		/// </summary>
		public void Reset()
		{
			this.Cursor = new byte[this.Length];
		}

		/// <summary>
		/// Advanced cursor to the next position.
		/// </summary>
		public void AdvanceCursor()
		{
			this.Cursor[this.Length - 1]++;
		}

		/// <summary>
		/// 
		/// </summary>
		public bool NormalizeCursor()
		{
			for (int i = this.Length - 1; i > 0; i--)
			{
				if (this.Cursor[i] >= this.Charset.Length)
				{
					this.Cursor[i] = 0;
					this.Cursor[i - 1]++;
				}
			}

			return this.Cursor[0] < this.Charset.Length;
		}

		public IEnumerator<string> GetEnumerator()
		{
			return new PermutationEnumerator(this);
		}

		System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
		{
			return this.GetEnumerator();
		}
	}
}
